#pragma once
#include <functional>
#include "sock.hpp"

namespace select_ns
{
    static const int defaultport = 8081;
    static const int defaultfd = -1;
    static const int fdnum = sizeof(fd_set) * 8;

    using func_t = std::function<std::string(const std::string &)>;
    class SelectServer
    {
    private:
        int _port;
        int _listensock;
        int *_fdarray; // 由程序员维护的合法fd数组
        func_t _func;

    public:
        SelectServer(func_t func, uint16_t port = defaultport)
            : _port(port),
              _listensock(-1),
              _fdarray(nullptr),
              _func(func)
        {
        }

        void initServer()
        {
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);
            _fdarray = new int[fdnum];
            // 设置为默认fd
            for (int i = 0; i < fdnum; ++i)
            {
                _fdarray[i] = defaultfd;
            }
            _fdarray[0] = _listensock;
            logMessage(NORMAL, "create listen sock success %d\n", _listensock);
        }

        void start()
        {
            while (1)
            {
                fd_set rfds; // 关心读事件
                // fd_set wfds;
                FD_ZERO(&rfds); // 文件描述符集合清零，每次循环都要清零
                // 最大的文件描述符，用于select传参
                int maxFd = defaultfd;
                for (int i = 0; i < fdnum; ++i)
                {
                    if (_fdarray[i] == defaultfd)
                        continue;
                    FD_SET(_fdarray[i], &rfds); // 将合法文件描述符设置进读文件描述符集
                    // maxFd = max(maxFD, _fdarray[i]);
                    if (maxFd < _fdarray[i])
                        maxFd = _fdarray[i];
                }
                // select多路转接
                // 工作原理是在指定的时间内轮询监视的文件描述符
                struct timeval time = {2, 0}; // 最多阻塞等待2秒就让select返回
                int n = select(maxFd + 1, &rfds, nullptr, nullptr, &time);// time会从2减到0，不更新就一直是0
                // int n = select(maxFd + 1, &rfds, nullptr, nullptr, nullptr); // 阻塞式等待
                switch (n)
                {
                case 0: // 超时返回
                    logMessage(NORMAL, "timeOut...");
                    break;
                case -1: // select失败
                    logMessage(WARNING, "select error, code: %d, err string:%s", errno, strerror(errno));
                    break;
                default:
                    // 事件就绪
                    // 如果没有把底层的连接取走，那么_listenSocket将一直就绪
                    // 会一直打印
                    logMessage(NORMAL, "have event ready");
                    handlerEvent(rfds);
                    // HandlerWriteEvent(wfds)//写事件回调
                    // HandlerErrorEvent(efds)//错误事件回调
                    break;
                }
            }
        }
        void handlerEvent(fd_set &rfds)
        {
            for (int i = 0; i < fdnum; ++i)
            {
                // 跳过不合法的
                if (_fdarray[i] == defaultfd)
                    continue;
                // 合法的不一定就绪了
                // 是监听套接字就用Listen处理
                if (FD_ISSET(_fdarray[i], &rfds) && _fdarray[i] == _listensock)
                {
                    Listen(_listensock);
                }
                else if (FD_ISSET(_fdarray[i], &rfds))
                {
                    Read(_fdarray[i], i);
                }
            }
        }
        void Listen(int listensock)
        {
            // 1.处理_listensock
            std::string clientip;
            uint16_t clientport = -1;
            // listensock只会在有新连接请求时变为就绪状态
            // accept之后就不是就绪态了
            int sock = Sock::Accept(listensock, &clientip, &clientport);
            if (sock < 0)
                return;
            // 2.将新的fd加入到_fdarray中
            int i = 0;
            // 找空的位置给新的sock
            while (i < fdnum && _fdarray[i] != defaultfd)
            {
                ++i;
            }
            if (i == fdnum)// 满了
            {
                logMessage(WARNING, "Server is full");
                close(sock);
            }
            else
            {
                _fdarray[i] = sock;
            }
        }
        void Read(int sock, int pos)
        {
            // 1、读取request（这样读取存在问题，无法保证读到一个完整报文）
            // 并且同时可能有多个文件描述符就绪，同时调用这段代码
            char buffer[1024];
            ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
            if (s > 0)
            {
                buffer[s] = 0;
                logMessage(NORMAL, "client# %s", buffer);
            }
            else if (0 == s) // 连接关闭
            {
                close(sock); // close之后，需要在所维护的数组中去掉对应的fd
                _fdarray[pos] = defaultfd;
                logMessage(NORMAL, "client quit");
                return;
            }
            else // 读取失败
            {
                close(sock); // close之后，需要在所维护的数组中去掉对应的fd
                _fdarray[pos] = defaultfd;
                logMessage(ERROR, "read fail%s", strerror(errno));
                return;
            }
            // 2、处理request
            std::string response = _func(buffer);
            // 3、返回response
            // 写事件，有问题的，需要重新维护一个写就绪的文件描述符数组
            // 需要保证sock写就绪
            write(sock, response.c_str(), response.size());
        }

        ~SelectServer()
        {
            if (_listensock > 0)
            {
                close(_listensock);
            }
            if (_fdarray)
            {
                for (int i = 0; i < fdnum; ++i)
                {
                    if (_fdarray[i] != defaultfd)
                    {
                        close(_fdarray[i]);
                    }
                }
                delete[] _fdarray;
            }
        }
    };
}